<?php
declare (strict_types=1);

namespace app\exam\logic;

use app\common\exception\LogicException;
use app\common\helper\util\SnowFlake;
use app\course\logic\coupon\CouponUser;
use app\exam\logic\admin\PaperStudent;
use app\common\logic\{CreditRedeem, Transaction};
use app\common\model\ProductType;
use app\exam\model\{Order as OrderModel, OrderItem as OI, PaperConfig};
use app\user\logic\user\Credit;
use think\Model;

/**
 * 订单逻辑类
 */
class Order extends OrderModel
{

    /**
     * 购买试卷渲染页面
     * Author   亓官雨树<lucky.max@foxmail.com>
     * Date     21/03/30
     *
     * @param $param
     * @return mixed
     */
    public function orderPaperFetch($param)
    {
        // 验证数据
        $this->getValidate()->scene('paper_fetch')->failException()->check($param);
        // 参数
        $paperId       = intval($param['paper_id']);
        $examinationId = intval($param['id']);

        $paper = app(Paper::class)
            ->with(['examination' => function ($query) use ($examinationId) {
                $query->where('id', $examinationId);
            }])
            ->field('id,title')
            ->where('id', $paperId)
            ->findOrFail();
        // 考试配置
        $config                         = app(PaperConfig::class)->where('data_key', 'examination_' . $paper['module_id'])->where('paper_id', $paperId)->field('cnf_data')->findOrEmpty();
        $paper['price']                 = $config['cnf_data']['price'] ?? 0;
        $paper['credit_redeem']         = $config['cnf_data']['credit_redeem'] ?? 0;
        $paper['credit_redeem_percent'] = $config['cnf_data']['credit_redeem_percent'] ?? 0;
        $paper->credit_arr              = [];
        if ($paper->credit_redeem) {
            $max_price         = intval(bcdiv(bcmul(strval($paper->user_price), strval($paper->credit_redeem_percent), 2), '100'));
            $paper->credit_arr = app(Credit::class)->getUserCreditRedeemArr($max_price, $paper->user_price);
        }
        return $paper;
    }

    /**
     * 创建考试订单
     * Author   亓官雨树<lucky.max@foxmail.com>
     * Date 2020-04-24
     */
    public function createPaperOrder($param)
    {
        // 验证数据
        $this->getValidate()->scene('paper_order')->failException()->check($param);
        // 参数
        $examinationId = $param['id'];
        $paperId       = $param['paper_id'];
        $credit        = $param['credit'] ?? ['credit' => 0, 'num' => 0];
        $credit        = is_array($credit) ? $credit : json_decode($credit, true);
        if (!isset($credit['credit']) || !isset($credit['num'])) throw new LogicException('积分抵扣参数错误');
        // 数据
        $exam = app(Examination::class)->with(['paper_mhm'])->field('id,module_id,paper_id')->where('id', $examinationId)->where('paper_id', $paperId)->findOrFail()->append(['user_price']);
        // 考试配置
        $config                        = app(PaperConfig::class)->where('data_key', 'examination_' . $exam['module_id'])->where('paper_id', $paperId)->field('cnf_data')->findOrEmpty();
        $exam['price']                 = $config['cnf_data']['price'] ?? 0;
        $exam['credit_redeem']         = $config['cnf_data']['credit_redeem'] ?? 0;
        $exam['credit_redeem_percent'] = $config['cnf_data']['credit_redeem_percent'] ?? 0;
        // 处理免费
        if ($exam['user_price'] == 0 || $exam['user_price'] == '0.00') throw new LogicException('免费考试不需要购买');
        // 产品类型ID
        $exam_type_id = ProductType::getProductIdByKey('exam');
        // 检查重复订单
        $this->orderExist($exam->id, $exam_type_id);
        // 生成订单
        // 返回订单
        return $this->createOrder($exam, $exam_type_id, $credit);
    }

    /**
     * 购买专项渲染页面
     * Author   亓官雨树<lucky.max@foxmail.com>
     * Date     21/03/30
     *
     * @param $param
     * @return mixed
     */
    public function orderSpecialFetch($param)
    {
        // 验证数据
        $this->getValidate()->scene('special_fetch')->failException()->check($param);
        // 参数
        $special_id = intval($param['special_id']);

        $special             = app(Special::class)
            ->field('id,title,price,credit_redeem,credit_redeem_percent')
            ->where('id', $special_id)
            ->findOrFail();
        $special->credit_arr = [];
        if ($special->credit_redeem) {
            $max_price           = intval(bcdiv(bcmul(strval($special->user_price), strval($special->credit_redeem_percent), 2), '100'));
            $special->credit_arr = app(Credit::class)->getUserCreditRedeemArr($max_price, $special->user_price);
        }

        return $special;
    }

    /**
     * 创建专项订单
     * Author   亓官雨树<lucky.max@foxmail.com>
     * Date 2020-04-24
     */
    public function createSpecialOrder($param)
    {
        // 验证数据
        $this->getValidate()->scene('special_order')->failException()->check($param);
        // 参数
        $special_id = $param['special_id'];
        $credit     = $param['credit'] ?? ['credit' => 0, 'num' => 0];
        $credit     = is_array($credit) ? $credit : json_decode($credit, true);
        if (!isset($credit['credit']) || !isset($credit['num'])) throw new LogicException('积分抵扣参数错误');
        // 数据
        $special = app(Special::class)->field('id,title,price,module_id,credit_redeem,credit_redeem_percent')->with(['mhm_id'])->where('id', $special_id)->findOrFail();
        // 处理免费
        if ($special['user_price'] == 0 || $special['user_price'] == '0.00') throw new LogicException('免费专项练习不需要购买');
        // 产品类型ID
        $special_type_id = ProductType::getProductIdByKey('special');
        // 检查重复订单
        $this->orderExist($special_id, $special_type_id);
        // 生成订单
        // 返回订单
        return $this->createOrder($special, $special_type_id, $credit);
    }

    /**
     * 购买套卷渲染页面
     * Author   亓官雨树<lucky.max@foxmail.com>
     * Date     21/03/30
     *
     * @param $param
     * @return mixed
     */
    public function orderRollupFetch($param)
    {
        // 验证数据
        $this->getValidate()->scene('rollup_fetch')->failException()->check($param);
        // 参数
        $rollup_id = intval($param['rollup_id']);

        $rollup             = app(Rollup::class)
            ->field('id,title,price,credit_redeem,credit_redeem_percent')
            ->where('id', $rollup_id)
            ->findOrFail();
        $rollup->credit_arr = [];
        if ($rollup->credit_redeem) {
            $max_price          = intval(bcdiv(bcmul(strval($rollup->user_price), strval($rollup->credit_redeem_percent), 2), '100'));
            $rollup->credit_arr = app(Credit::class)->getUserCreditRedeemArr($max_price, $rollup->user_price);
        }

        return $rollup;
    }

    /**
     * 创建套卷订单
     * Author   亓官雨树<lucky.max@foxmail.com>
     * Date 2020-04-24
     */
    public function createRollupOrder($param)
    {
        // 验证数据
        $this->getValidate()->scene('rollup_order')->failException()->check($param);
        // 参数
        $rollup_id = $param['rollup_id'];
        $credit    = $param['credit'] ?? ['credit' => 0, 'num' => 0];
        $credit    = is_array($credit) ? $credit : json_decode($credit, true);
        if (!isset($credit['credit']) || !isset($credit['num'])) throw new LogicException('积分抵扣参数错误');
        // 数据
        $rollup = app(Rollup::class)->field('id,title,price,module_id,credit_redeem,credit_redeem_percent')->with(['mhm_id'])->where('id', $rollup_id)->findOrFail();
        // 处理免费
        if ($rollup['user_price'] == 0 || $rollup['user_price'] == '0.00') throw new LogicException('免费套卷不需要购买');
        // 产品类型ID
        $rollup_type_id = ProductType::getProductIdByKey('rollup');
        // 检查重复订单
        $this->orderExist($rollup_id, $rollup_type_id);
        // 生成订单
        // 返回订单
        return $this->createOrder($rollup, $rollup_type_id, $credit);
    }

    /**
     * 检查重复订单
     * Author   亓官雨树<lucky.max@foxmail.com>
     * Date 2020-04-27
     */
    public function orderExist($product_id, $product_type_id)
    {
        // 检测重复订单
        $order = app(OI::class)
            ->whereUserId(request()->mid)
            ->whereProductId($product_id)
            ->whereProductTypeId($product_type_id)
            ->with(['order_status', 'coupon_id'])
            ->order('order_no', 'DESC')
            ->findOrEmpty();

        if (!$order->isEmpty()) {
            if (in_array($order['status'], [30, 40])) {
                throw new LogicException('订单已存在', '已有商品订单存在');
            } else if ($order['status'] == 10) {
                $data = $this->with(['redeem_credit_sub'])->findOrEmpty($order['order_no']);
                if ($data->isEmpty()) return;
                $data->status = 60;
                if (!$data->save()) throw new LogicException('订单已存在', '已有商品订单存在');
                // 处理积分退还
                if (count($data->redeem_credit_sub)) {
                    app(Credit::class)->returnOrderCredit($data);
                }
                // 处理卡券退还
                if ($order->coupon_id) {
                    app(CouponUser::class)->returnOrderCoupon($order->user_id, $order->coupon_id);
                }
            } else if ($order['status'] == 20) {
                throw new LogicException('已购买');
            }
        }
    }

    /**
     * 内部创建订单方法
     * Author   亓官雨树<lucky.max@foxmail.com>
     * Date     21/03/30
     *
     * @param       $data
     * @param       $product_type_id
     * @param array $credit
     * @return Order|Model
     */
    private function createOrder($data, $product_type_id, array $credit = [])
    {
        // 检查积分抵扣是否符合
        if ($data->credit_redeem && $credit['num']) {
            // 课程最大抵扣金额
            $max_price = intval($data->user_price * $data->credit_redeem_percent / 100);
            // 积分抵扣实例
            $redeem = new CreditRedeem(request()->user_id, $max_price);
            // 未配置积分抵扣
            if (!$redeem->hasRedeemRatio()) throw new LogicException('积分抵扣参数错误.');
            // 抵扣金额超限
            $max_redeem_num = $redeem->getMaxRedeemNum();

            if ($credit['num'] > $max_redeem_num) {
                throw new LogicException('积分抵扣参数错误..');
            }
            if (!$redeem->isCreditMatchNum($credit)) {
                throw new LogicException('积分抵扣参数错误...');
            }
        }

        $this->startTrans();
        try {
            // 主订单
            $PD         = app(SnowFlake::class)->createPD();
            $com_save   = [
                'order_no' => $PD,
                'user_id'  => request()->mid,
                'from'     => request()->device_type,
            ];
            $payment    = round($data->user_price - $credit['num'], 2);
            $order_save = [
                'payment' => $payment,
                'status'  => $payment ? '10' : '20',
                // 'close_time' => time() + 30 * 60,
                'mhm_id'  => request()->user->mhm_id,
            ];
            $order      = $this::create(array_merge($com_save, $order_save));

            if ($order->isEmpty()) {
                throw new LogicException('订单生成失败', '主订单生成失败');
            }
            $item_save  = [
                'order_sub_no'    => app(SnowFlake::class)->createPI(),
                'product_id'      => $data->id,
                'product_type_id' => $product_type_id,
                'payment'         => $payment,
                'original_price'  => $data->price,
                'mhm_id'          => $data->mhm_id,
                'credit'          => $credit
            ];
            $order_item = OI::create(array_merge($com_save, $item_save));
            if ($order_item->isEmpty()) {
                throw new LogicException('订单生成失败', '子订单生成失败');
            }
            if (!$payment) {
                app(Transaction::class)->addTransaction($order, 'lcnpay', true);
            }
            // 抵扣积分
            if (count($credit) && isset($credit['credit']) && $credit['credit']) {
                app(Credit::class)->redeemCredit(intval($credit['credit']), $order->order_no);
            }
            $this->commit();
        } catch (LogicException $e) {
            $this->rollback();
            throw new LogicException($e->getMessage());
        }
        // 订单需要支付
        $order->need_pay = $payment ? 1 : 0;
        // 返回订单
        return $order->visible(['order_no', 'from', 'payment', 'status', 'close_time', 'need_pay']);
    }

}
